/*
 * Copyright 2013-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cloud.gateway.handler;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import reactor.core.publisher.Mono;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.core.DecoratingProxy;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebHandler;

import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;

/**
 * WebHandler that delegates to a chain of {@link GlobalFilter} instances and
 * {@link GatewayFilterFactory} instances then to the target {@link WebHandler}.
 * webHander 委托 到一个 过滤器链
 * @author Rossen Stoyanchev
 * @author Spencer Gibb
 * @author Yuxin Wang
 * @since 0.1
 *
 * FilteringWebHandler 是 Spring Cloud Gateway 中的一个关键组件，它负责处理 HTTP 请求并应用相应的过滤器链。在 Spring Cloud Gateway 的架构中，FilteringWebHandler 作为核心处理器之一，用于管理请求的路由和过滤逻辑。
 * 核心概念
 * 		Handler：在 Spring WebFlux 中，Handler 是用于处理传入 HTTP 请求的组件。
 * 		Filters：Spring Cloud Gateway 支持多种类型的过滤器（GatewayFilter 和 GlobalFilter），它们可以在请求到达目标服务之前或之后对请求进行修改。
 * 		Routing：根据匹配的路由规则，将请求转发到指定的目标服务。
 *
 *工作原理
 * 当一个 HTTP 请求进入网关时，RoutePredicateHandlerMapping 首先会根据请求信息匹配合适的路由。一旦找到匹配的路由，就会使用 FilteringWebHandler 来处理该请求。以下是简化的工作流程：
 * 		路由匹配：通过 RoutePredicateHandlerMapping 找到与请求匹配的路由。
 * 		获取过滤器链：对于每个匹配的路由，都会有一个关联的过滤器链（由 GatewayFilter 和 GlobalFilter 组成）。
 * 		执行过滤器：FilteringWebHandler 依次调用过滤器链中的每个过滤器，每个过滤器都可以选择继续传递请求或直接终止处理。
 * 		转发请求：如果所有过滤器都正常执行完毕，则请求会被转发到目标 URI。
 */
public class FilteringWebHandler implements WebHandler {

	protected static final Log logger = LogFactory.getLog(FilteringWebHandler.class);

	private final List<GatewayFilter> globalFilters;

	public FilteringWebHandler(List<GlobalFilter> globalFilters) {
		this.globalFilters = loadFilters(globalFilters);
	}

	private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) {
		return filters.stream().map(filter -> {
			GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter);
			if (filter instanceof Ordered) {
				int order = ((Ordered) filter).getOrder();
				return new OrderedGatewayFilter(gatewayFilter, order);
			}
			else {
				Order order = AnnotationUtils.findAnnotation(filter.getClass(), Order.class);
				if (order != null) {
					return new OrderedGatewayFilter(gatewayFilter, order.value());
				}
			}
			return gatewayFilter;
		}).collect(Collectors.toList());
	}

	/*
	 * TODO: relocate @EventListener(RefreshRoutesEvent.class) void handleRefresh() {
	 * this.combinedFiltersForRoute.clear();
	 */

	/**
	 * 这段Java代码是在一个WebFlux（反应式编程模型）的上下文中，特别是在Spring Cloud Gateway中处理HTTP请求的一部分。Spring Cloud Gateway是一个基于WebFlux的API网关，它提供了一个简单而有效的方式来路由到APIs，并为它们提供跨领域的关注点（如安全、监控/指标、弹性等）。
	 * 让我们逐步解释这段代码：
	 * 方法签名:
	 * @Override: 表示这个方法是重写了父类或接口中的方法。
	 * public Mono<Void> handle(ServerWebExchange exchange): 这是一个公开的方法，返回一个Mono<Void>类型的对象，表示这是一个异步操作。
	 * 它接受一个ServerWebExchange参数，这是Spring WebFlux中表示当前服务器Web交互的上下文对象。
	 * 获取路由信息:
	 * 		Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);: 从ServerWebExchange中获取当前请求匹配的路由信息。GATEWAY_ROUTE_ATTR是一个常量，表示路由属性的键。
	 * 获取过滤器列表:
	 *		List<GatewayFilter> gatewayFilters = route.getFilters();: 从路由信息中获取与该路由相关联的过滤器列表。GatewayFilter是用于处理请求和响应的接口。
	 * 合并全局和路由过滤器:
	 * 		创建一个新的ArrayList，命名为combined，并将全局过滤器this.globalFilters添加到其中。
	 * 		使用combined.addAll(gatewayFilters);将路由特定的过滤器也添加到这个列表中。
	 * 排序过滤器:
	 * 		AnnotationAwareOrderComparator.sort(combined);: 使用AnnotationAwareOrderComparator对合并后的过滤器列表进行排序。这个比较器会尊重@Order注解或者实现Ordered接口的顺序。
	 * 日志记录:
	 * 		如果启用了调试日志，那么会记录排序后的过滤器列表。
	 * 创建并返回过滤器链:
	 * 		return new DefaultGatewayFilterChain(combined).filter(exchange);:
	 * 		使用排序后的过滤器列表创建一个DefaultGatewayFilterChain对象，
	 * 		并调用其filter方法来应用这些过滤器到当前的ServerWebExchange。
	 * 		这个调用是异步的，返回一个Mono<Void>，
	 * 		表示过滤操作完成时的信号。
	 * 总结: 这段代码是Spring Cloud Gateway中处理HTTP请求的核心部分之一，它负责将全局过滤器和路由特定的过滤器合并、排序，并应用到请求上。通过这种方式，Spring Cloud Gateway可以灵活地处理各种跨领域的关注点，如安全、日志记录、请求修改等，而这些关注点可以通过简单的过滤器实现来添加到请求处理流程中。
	 *
	 * 为当前代码库更新索引
	 * @param exchange
	 * @return
	 */
	@Override
	public Mono<Void> handle(ServerWebExchange exchange) {
		Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
		List<GatewayFilter> gatewayFilters = route.getFilters();

		List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
		combined.addAll(gatewayFilters);
		// TODO: needed or cached?
		AnnotationAwareOrderComparator.sort(combined);
		if (logger.isDebugEnabled()) {
			logger.debug("Sorted gatewayFilterFactories: " + combined);
		}
		return new DefaultGatewayFilterChain(combined).filter(exchange);
	}

	private static class DefaultGatewayFilterChain implements GatewayFilterChain {

		private final int index;
		private final List<GatewayFilter> filters;

		DefaultGatewayFilterChain(List<GatewayFilter> filters) {
			this.filters = filters;
			this.index = 0;
		}

		private DefaultGatewayFilterChain(DefaultGatewayFilterChain parent, int index) {
			this.filters = parent.getFilters();
			this.index = index;
		}

		public List<GatewayFilter> getFilters() {
			return filters;
		}

		@Override
		public Mono<Void> filter(ServerWebExchange exchange) {
			return Mono.defer(() -> {
				if (this.index < filters.size()) {
					GatewayFilter filter = filters.get(this.index);
					DefaultGatewayFilterChain chain = new DefaultGatewayFilterChain(this, this.index + 1);
					return filter.filter(exchange, chain);
				}
				else {
					return Mono.empty(); // complete
				}
			});
		}

	}

	private static class GatewayFilterAdapter implements GatewayFilter, DecoratingProxy {

		private final GlobalFilter delegate;

		GatewayFilterAdapter(GlobalFilter delegate) {
			this.delegate = delegate;
		}

		@Override
		public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
			return this.delegate.filter(exchange, chain);
		}

		@Override
		public String toString() {
			final StringBuilder sb = new StringBuilder("GatewayFilterAdapter{");
			sb.append("delegate=").append(delegate);
			sb.append('}');
			return sb.toString();
		}

		@Override
		public Class<?> getDecoratedClass() {
			return delegate.getClass();
		}

	}

}
